package com.example.demo.aop;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.stream.Collectors;


/**
 * 系统日志，切面处理类
 *
 * @author jie
 * @date 2019/12/23 -13:36
 */
@Aspect
@Component
public class AutoStringAspect {

    private static final String DTO_PATH = "com.example.demo.dto";

    // 通过 Pointcut 获取注解位置
//    @Pointcut("@annotation(com.cloudlinkscm.annotation.AutoString)")
    @Pointcut("execution(* com.example.demo.controller..*.*(..))")
    public void stringPointCut() {

    }

    // 增强处理
    @Around("stringPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {


        //获取参数值
        Object[] paramValues = point.getArgs();
        for (Object arg : paramValues) {

            // 入参是一个并且是对象类型
            if (paramValues.length == 1 && arg.getClass() != null) {

                String className = arg.getClass().getName();
                String packageName = getPackageName(className);

                //如果包是DTO层的包
                if (DTO_PATH.equals(packageName)) {
                    setValueForObjectType(arg);
                }
            }

        }

        return point.proceed();
    }

    private void setValueForObjectType(Object arg) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        //获取类的字段
        Field[] fields = arg.getClass().getDeclaredFields();
        for (Field field : fields) {

            // 设置字段可访问， 否则无法访问private修饰的变量值
            field.setAccessible(true);

            //获取字段名
            String name = field.getName();
            // 获取字段类型
            String type = field.getGenericType().toString();

            // 拼装get set方法  例如 setName getName
            String setMethodName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
            String getMethodName = "get" + name.substring(0, 1).toUpperCase() + name.substring(1);


            //通过方法名称获取对应的方法，这里是获取字段的get方法
            Method getMethod = arg.getClass().getMethod(getMethodName);

            // 判断类型是string 类型
            if ("class java.lang.String".equals(type)) {

                // 获取set方法
                Method setMethod = arg.getClass().getMethod(setMethodName, String.class);
                // 通过 invoke 获取到get方法的Value值  PS: invoke()方法就是用来执行指定对象的方法
                String value = (String) getMethod.invoke(arg);
                // 再invoke 执行 set方法  StringUtils.trim 对空格进行处理
                setMethod.invoke(arg, StringUtils.trim(value));
            }

            // 判断类型是List<String> 类型
            if ("java.util.List<java.lang.String>".equals(type)) {

                Method setMethod = arg.getClass().getMethod(setMethodName, List.class);
                List<String> valueList = (List<String>) getMethod.invoke(arg);
                // 通过java 8 对集合中的值进行处理 ，java8 中 “：：” 就是调用方法的意思
                List<String> collect = valueList.stream().map(StringUtils::trim).collect(Collectors.toList());
                setMethod.invoke(arg, collect);

            }
        }
    }

    private String getPackageName(String className) {
        StringBuilder sb = new StringBuilder();

        String[] classNameSize = className.split("\\.");

        String[] dtoPathSize = DTO_PATH.split("\\.");

        // 获取DTO包的长度相等的包路径
        for (int i = 0; i < classNameSize.length; i++) {
            if (i < dtoPathSize.length) {
                if (i != 0) {
                    sb.append(".");
                }
                sb.append(classNameSize[i]);
            }
        }
        return sb.toString();
    }

}